package com.ehotting.edsta.circuitDesign.gen;

import com.alibaba.fastjson.JSONObject;
import com.ehotting.eaf.core.utils.StringUtils;
import com.ehotting.edsta.circuitDesign.bean.CircuitElementBean;
import com.ehotting.edsta.circuitDesign.bean.CircuitGroupBean;
import com.ehotting.edsta.circuitDesign.bean.request.RequestCodeExecute;
import com.ehotting.edsta.circuitDesign.exception.LoggerTar;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.NumberUtils;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class CircuitAllocationHelper {

    private static Logger logger = LoggerFactory.getLogger(CircuitAllocationHelper.class);

    private final static Integer EXPONENT_LEVEL = 4;

    private final static Integer QUERY_SIZE = 10;

    public static List<CircuitGroupBean> invoke(
            List<RequestCodeExecute.InputChannel> analogueChannelList,
            List<CircuitElementBean> allCircuitElementList) {

        List<CircuitGroupBean> circuitGroupList = findCircuitGroupList(
                 analogueChannelList,
                 allCircuitElementList);

        circuitGroupList = circuitGroupList.stream()
                .sorted((a, b) -> a.getAllChannelCount() - b.getAllChannelCount())
                .sorted((a, b) -> a.getMaxSample() - b.getMaxSample())
                .filter(c -> c.getAllOutChannelCount4Analogue() < 24)
                .limit(10)
                .collect(Collectors.toList());

        circuitGroupList = circuitGroupList.stream()
                .sorted((a, b) -> a.getCircuitCount() - b.getCircuitCount())
                .limit(3)
                .collect(Collectors.toList());

        return circuitGroupList;
    }

    /***
     *  按顺序根据指数值从4到0查询，如果满足数量@link{QUERY_SIZE}，则直接返回,否则执行指数到为0
     * @param analogueChannelList
     * @param allCircuitElementList
     * @return
     */
    private static List<CircuitGroupBean> findCircuitGroupList(
            List<RequestCodeExecute.InputChannel> analogueChannelList,
            List<CircuitElementBean> allCircuitElementList){

        List<CircuitGroupBean> circuitGroupList = new ArrayList<>();

        for (int i = EXPONENT_LEVEL; i >= 0; i--) {
            List<CircuitGroupBean> cgtList = getCircuitGroupByRecursion(
                    analogueChannelList,
                    allCircuitElementList,
                    i, 0);

            /**过滤结果**/
            cgtList = filterDistinct(cgtList);
            /**加入circuitGroupList结果**/
            circuitGroupList.addAll(cgtList);
            /**过滤结果**/
            circuitGroupList = filterDistinct(circuitGroupList);
            /**如果数据达到QUERY_SIZE的数重，直接跳出循环**/
            if (circuitGroupList.size() >= QUERY_SIZE) {
                break;
            }
        }
        return circuitGroupList;
    }

    /**
     * 过滤重复数据
     * @param circuitGroupList
     * @return
     */
    private static List<CircuitGroupBean> filterDistinct(List<CircuitGroupBean> circuitGroupList) {
        Map<String, Object> exist = new HashMap<>();
        for (CircuitGroupBean cgt : circuitGroupList) {
            exist.put(cgt.getIdAndCountKey(), cgt);
        }
        return new ArrayList(exist.values());
    }


    /**
     * 通过递归方式组合通道分组
     * @param analogueChannelList
     * @param allCircuitElementList
     * @param channelExponent
     * @param exponentLevel
     * @return
     */
    private static List<CircuitGroupBean> getCircuitGroupByRecursion(List<RequestCodeExecute.InputChannel> analogueChannelList,
                                                                     List<CircuitElementBean> allCircuitElementList,
                                                                     Integer channelExponent,
                                                                     Integer exponentLevel) {
        if (channelExponent < 0) {
            return new ArrayList<CircuitGroupBean>();
        }

        Integer channelStepNum = (int) Math.pow(2, channelExponent);

        List<List<RequestCodeExecute.InputChannel>> groupList = groupingChannelByStep(analogueChannelList, channelStepNum);

        List<List<CircuitGroupBean>> cgList = new ArrayList<>();
        for (int i = 0; i < groupList.size(); i++) {
            List<RequestCodeExecute.InputChannel> ctList = groupList.get(i);
            if (ctList.size() == 0) {
                continue;
            }
            List<CircuitGroupBean> circuitGroupList = createCircuitGroup(ctList, allCircuitElementList, exponentLevel);
            if (circuitGroupList.size() == 0) {
                circuitGroupList = getCircuitGroupByRecursion(ctList, allCircuitElementList, --channelExponent, exponentLevel);
            }
            if (circuitGroupList.size() == 0) {
                return new ArrayList<>();
            }

            cgList.add(circuitGroupList);
        }
        return cross(cgList);

    }

    /**
     * 将二维数据组转为一维数组
     * 比如[[a1,a2][b1,b2]]转换为[a1,b1][a1,b2][a2,b1][a2,b2]
     *
     * @param cgList
     * @return
     */
    private static List<CircuitGroupBean> cross(List<List<CircuitGroupBean>> cgList) {
        List<CircuitGroupBean> newCgList = new ArrayList();
        newCgList.add(new CircuitGroupBean());
        for (List<CircuitGroupBean> ct : cgList) {
            newCgList = cross(newCgList, ct);
        }
        return newCgList;
    }

    /**
     * 使用*运算符来解包原始二维数组，使用嵌套循环来完成
     * 比如[[a1,a2][b1,b2]]转换为[a1,b1][a1,b2][a2,b1][a2,b2]
     *
     * @param cgList1
     * @param cgList2
     * @return
     */
    private static List<CircuitGroupBean> cross(
            List<CircuitGroupBean> cgList1,
            List<CircuitGroupBean> cgList2) {
        List<CircuitGroupBean> newCircuitGroupList = new ArrayList<>();
        for (CircuitGroupBean cgt1 : cgList1) {
            for (CircuitGroupBean cgt2 : cgList2) {
                CircuitGroupBean newCircuitGroup = new CircuitGroupBean();
                newCircuitGroup.putAll(cgt1.clone());
                newCircuitGroup.putAll(cgt2.clone());
                newCircuitGroupList.add(newCircuitGroup);
            }
        }
        return newCircuitGroupList;
    }


    /***
     * 查询CircuitElementBean，并组合为CircuitGroupBean
     * @param analogueChannelList
     * @param allCircuitElementList
     * @param exponentLevel
     * @return
     */
    private static List<CircuitGroupBean> createCircuitGroup(List<RequestCodeExecute.InputChannel> analogueChannelList,
                                                             List<CircuitElementBean> allCircuitElementList,
                                                             Integer exponentLevel) {

        if (analogueChannelList.size() == 0) {
            return new ArrayList<>();
        }

        /**根据通道相等，查询电路**/
        List<CircuitElementBean> circuitElementList = doFindCircuitByEqualChannel(analogueChannelList, allCircuitElementList);
        /**根据通道数量接近[16，8，4，2，1]来查询电路**/
        doFindCircuitByRangChannel(analogueChannelList, allCircuitElementList,circuitElementList);
        /**根据通道逐步增加来查询电路，比如[1,2,4,8,16]**/
        if (circuitElementList.size() == 0){
            exponentLevel = 2;
        }
        doFindCircuitByUpChannel(analogueChannelList, allCircuitElementList, circuitElementList, exponentLevel);

        /**CircuitElementBean列表转为CircuitGroupBean列表**/
        List<CircuitGroupBean> circuitGroupList = convert(circuitElementList, analogueChannelList);

        return circuitGroupList;
    }

    private static List<CircuitGroupBean> convert(List<CircuitElementBean> circuitElementList,
                                           List<RequestCodeExecute.InputChannel> analogueChannelList){
        Integer inputChannelSize = analogueChannelList.size();
        List<CircuitGroupBean> circuitGroupList = circuitElementList.stream().map(t -> {
            CircuitGroupBean cGroup = new CircuitGroupBean();
            CircuitElementBean newCircuitTest = t.clone();
            newCircuitTest.setCount((int) Math.ceil(1.0 * inputChannelSize / t.getChannelNum()));

            newCircuitTest.setChannelList(new ArrayList<>(analogueChannelList));
            cGroup.put(t.getId(), newCircuitTest);
            return cGroup;
        }).collect(Collectors.toList());
        return circuitGroupList;

    }

    /***
     * 根据通道相等，查询电路
     * @param analogueChannelList
     * @param allCircuitElementList
     * @return
     */
    private static List<CircuitElementBean> doFindCircuitByEqualChannel(List<RequestCodeExecute.InputChannel> analogueChannelList,
                                                                        List<CircuitElementBean> allCircuitElementList) {

        List<CircuitElementBean> circuitElementList = findCircuitElementList(analogueChannelList, allCircuitElementList, 0, new ChannelJudgmentAble() {
            @Override
            public boolean invoke(CircuitElementBean t, Integer exponentIndex) {
                Integer inputChannelSize = analogueChannelList.size();
                return t.getChannelNum()==inputChannelSize;
            }
        });
        return circuitElementList;
    }

    /***
     * 根据通道数量接近[16，8，4，2，1]来查询电路
     * @param analogueChannelList
     * @param allCircuitElementList
     * @param circuitElementList
     * @return
     */
    private static List<CircuitElementBean> doFindCircuitByRangChannel(List<RequestCodeExecute.InputChannel> analogueChannelList,
                                                                       List<CircuitElementBean> allCircuitElementList,
                                                                       List<CircuitElementBean> circuitElementList){
        if (circuitElementList.size() >= QUERY_SIZE) {
            /**数量已经达到，直接返回**/
            return new ArrayList<>();
        }

        List<CircuitElementBean> ctList = findCircuitElementList(analogueChannelList,
                allCircuitElementList,
                0,
                new ChannelJudgmentAble() {
            @Override
            public boolean invoke(CircuitElementBean t, Integer exponentIndex) {

                Integer inputChannelSize = analogueChannelList.size();

                Integer exponent = (int) (Math.log(inputChannelSize) / Math.log(2));
                double median = 1.0 * (Math.pow(2, exponent) + Math.pow(2, exponent + 1)) / 2;
                boolean flag = false;
                if (inputChannelSize <= median) {
                    flag = inputChannelSize <= t.getChannelNum() && t.getChannelNum() <= median;
                }
                if ((!flag) && inputChannelSize >= median) {
                    return inputChannelSize <= t.getChannelNum() && t.getChannelNum() <= Math.pow(2, exponent + 1);
                }
                return flag;
            }
        });
        if (ctList.size() > 0) {
            circuitElementList.addAll(ctList);
        }
        return ctList;
    }


    /**
     * 指数exponentLevel往上增加查询
     * @param analogueChannelList
     * @param allCircuitElementList
     * @param circuitElementList
     * @param exponentLevel
     * @return
     */
    private static List<CircuitElementBean> doFindCircuitByUpChannel(List<RequestCodeExecute.InputChannel> analogueChannelList,
                                                                     List<CircuitElementBean> allCircuitElementList,
                                                                     List<CircuitElementBean> circuitElementList,
                                                                     Integer exponentLevel){

        if (circuitElementList.size() >= QUERY_SIZE || exponentLevel == 0) {
            /**数量已经达到，直接返回**/
            return new ArrayList<>();
        }

        for (Integer exponentIndex = 0; exponentIndex < exponentLevel; exponentIndex++) {
            List<CircuitElementBean> ctList = findCircuitElementList(analogueChannelList,
                    allCircuitElementList,
                    exponentIndex,
                    new ChannelJudgmentAble() {
                @Override
                public boolean invoke(CircuitElementBean t, Integer exponentIndex) {

                    Integer inputChannelSize = analogueChannelList.size();

                    Integer exponent = (int) (Math.ceil(Math.log(inputChannelSize) / Math.log(2))) + exponentIndex;
                    if (exponent >= 5) {/**最大16通道**/
                        return false;
                    }
                    return t.getChannelNum() >= inputChannelSize && t.getChannelNum() <= Math.pow(2, exponent);
                }

            });

            if (ctList.size() > 0) {
                circuitElementList.addAll(ctList);
            }

            if (circuitElementList.size() >= QUERY_SIZE) {
                break;
            }
        }

        return circuitElementList;
    }


    /**
     * 从@{allCircuitElementList}查找符合条件的电路
     * @param inputChannelList
     * @param allCircuitElementList
     * @param exponentIndex
     * @param channelJudgment
     * @return
     */
    private static List<CircuitElementBean> findCircuitElementList(
            List<RequestCodeExecute.InputChannel> inputChannelList,
            List<CircuitElementBean> allCircuitElementList,
            Integer exponentIndex,
            ChannelJudgmentAble channelJudgment){

        if(inputChannelList.get(0) instanceof RequestCodeExecute.AnalogueData){
            List<RequestCodeExecute.AnalogueData> analogueChannelList = inputChannelList
                    .stream()
                    .map(t->{return (RequestCodeExecute.AnalogueData)t; })
                    .collect(Collectors.toList());
            return findAnalogueCircuit(analogueChannelList,
                     allCircuitElementList,
                     exponentIndex,
                     channelJudgment);
        }

        List<RequestCodeExecute.DigitalData> analogueChannelList = inputChannelList
                .stream()
                .map(t->{return (RequestCodeExecute.DigitalData)t; })
                .collect(Collectors.toList());

        /*信号电压*/
        RequestCodeExecute.DigitalData maxInputVoltageRange = analogueChannelList.stream()
                .max((o1, o2) -> Integer.valueOf(o1.getInvolt()) - Integer.valueOf(o2.getInvolt()))
                .get();
        RequestCodeExecute.DigitalData minInputVoltageRange = analogueChannelList.stream()
                .min((o1, o2) -> Integer.valueOf(o1.getInvolt()) - Integer.valueOf(o2.getInvolt()))
                .get();

        List<CircuitElementBean> circuitElementList = allCircuitElementList.stream().filter(t->{
            boolean b = isInputVoltageRangeEquals(t, minInputVoltageRange, maxInputVoltageRange);
            if(b){
                logger.info("("+t.getCircuit().getName()+") isInputVoltageRangeEquals is true");
                return true;
            }
            return false;
        }
        ).collect(Collectors.toList());


        return circuitElementList;
    }

    private static List<CircuitElementBean> findAnalogueCircuit(List<RequestCodeExecute.AnalogueData> analogueChannelList,
                                                         List<CircuitElementBean> allCircuitElementList,
                                                         Integer exponentIndex,
                                                         ChannelJudgmentAble channelJudgment){

        /*采样率*/
        RequestCodeExecute.AnalogueData maxSampleAnalogueChannel = analogueChannelList
                .stream()
                .max((o1, o2) -> o1.getSample() - o2.getSample())
                .get();

        /*位数（精度）*/
        RequestCodeExecute.AnalogueData maxSamplingBitAnalogueChannel = analogueChannelList
                .stream()
                .max((o1, o2) -> Integer.valueOf(o1.getSamplingbit()) - Integer.valueOf(o2.getSamplingbit()))
                .get();

        /*信号带宽（频率）*/
        RequestCodeExecute.AnalogueData maxSignalAnalogueChannel = analogueChannelList
                .stream()
                .max((o1, o2) -> {
                    return Float.valueOf(o1.getSignalmax()) - Float.valueOf(o2.getSignalmax()) > 0 ? 1 : -1;
                })
                .get();
        RequestCodeExecute.AnalogueData minSignalAnalogueChannel = analogueChannelList
                .stream()
                .min((o1, o2) -> {
                    return Float.valueOf(o1.getSignalmin()) - Float.valueOf(o2.getSignalmin()) > 0 ? 1 : -1;
                })
                .get();


        RequestCodeExecute.AnalogueData inpuMaxRangeAnalogueChannel = analogueChannelList
                .stream()
                .max((o1, o2) -> {
                    return Double.valueOf(o1.getMaxdatarange()) - Double.valueOf(o2.getMaxdatarange()) > 0 ? 1 : -1;
                })
                .get();
        RequestCodeExecute.AnalogueData inpuMinRangeAnalogueChannel = analogueChannelList
                .stream()
                .min((o1, o2) -> {
                    return Double.valueOf(o1.getMindatarange()) - Double.valueOf(o2.getMindatarange()) > 0 ? 1 : -1;
                })
                .get();

        double maxSample = maxSampleAnalogueChannel.getSample();
        double maxSamplingBit = Double.valueOf(maxSamplingBitAnalogueChannel.getSamplingbit());
        Float maxSignal = Float.valueOf(maxSignalAnalogueChannel.getSignalmax());
        Float minSignal = Float.valueOf(minSignalAnalogueChannel.getSignalmax());
        double inputMinRange = Double.valueOf(inpuMinRangeAnalogueChannel.getMindatarange());
        double inputMaxRange = Double.valueOf(inpuMaxRangeAnalogueChannel.getMaxdatarange());


        List<CircuitElementBean> circuitElementList = allCircuitElementList.stream().filter(t->{
            return channelJudgment.invoke(t,exponentIndex)
                    && isSampleEquals(t,maxSample,maxSamplingBit,maxSignal,minSignal,inputMaxRange,inputMinRange);
        }).collect(Collectors.toList());
        return circuitElementList;

    }


    public interface ChannelJudgmentAble {
        public abstract boolean invoke(CircuitElementBean t,Integer exponentIndex);
    }

    private static boolean isSampleEquals(CircuitElementBean t,
                                          double maxSample,
                                          double maxSamplingBit,
                                          Float maxSignal,
                                          Float minSignal,
                                          double inputMaxRange,
                                          double inputMinRange
                                          ){
        if(Integer.valueOf(t.getChannelTypeOut().getValue()) == 0){
            return isSamplingBitEquals(t,maxSamplingBit,maxSignal,minSignal,inputMaxRange,inputMinRange);
        }
        Integer sample = t.getSample();
        if(sample >= maxSample) {
            return isSamplingBitEquals(t,maxSamplingBit,maxSignal,minSignal,inputMaxRange,inputMinRange);
        }
        return false;
    }

    private static boolean isSamplingBitEquals(CircuitElementBean t,
                                               double maxSamplingBit,
                                               Float maxSignal,
                                               Float minSignal,
                                               double inputMaxRange,
                                               double inputMinRange){
        if(Integer.valueOf(t.getChannelTypeOut().getValue()) == 0){
            return isSignalEquals(t,maxSignal,minSignal,inputMaxRange,inputMinRange);
        }
        Integer numberOfDigits = t.getNumberOfDigits();
        if(numberOfDigits >= maxSamplingBit) {
            return isSignalEquals(t,maxSignal,minSignal,inputMaxRange,inputMinRange);
        }
        return false;
    }

    private static boolean isSignalEquals(CircuitElementBean t,
                                          Float maxSignal,
                                          Float minSignal,
                                          double inputMaxRange,
                                          double inputMinRange){
        Float tFMax = t.getFMax();
        Float tFMin = t.getFMin();
        if (tFMax >= maxSignal && tFMin <= minSignal) return isInputRangeEquals(t,inputMaxRange,inputMinRange);
        return false;
    }

    private static boolean isInputRangeEquals(CircuitElementBean t,
                                              double inputMaxRange,
                                              double inputMinRange){
        double tMinRange = t.getInputMinRange();
        double tMaxRange = t.getInputMaxRange();
        if(tMinRange <= inputMinRange && tMaxRange >= inputMaxRange) {
            return true;
        }
        return false;
    }

    private static boolean isInputVoltageRangeEquals(CircuitElementBean t,
                                                     RequestCodeExecute.DigitalData min,
                                                RequestCodeExecute.DigitalData max){
        if(StringUtils.isEmpty(t.getInputVoltageRange())){
            return false;
        }

        double minInvolute = Double.valueOf(min.getInvolt());
        double maxInvolute = Double.valueOf(max.getInvolt());
        if(minInvolute == maxInvolute && maxInvolute <= 5){
           return inputVoltageRangeMatching(t,minInvolute);
        }

        if(t.getInputVoltageRange().indexOf("-") == -1) return false;

        String[] s = t.getInputVoltageRange().replace(" ", "").split("-");
        double[] sDouble = Arrays.stream(s).mapToDouble(Double::parseDouble).toArray();

        return sDouble[1] >= maxInvolute;
    }

    private static boolean inputVoltageRangeMatching(CircuitElementBean t,double maxInvolute){
        if(t.getInputVoltageRange().indexOf("-") != -1) return false;
        return Double.valueOf(t.getInputVoltageRange()) >= maxInvolute;
    }

    /***
     * 根据通道步长进行切割分组模拟通道列表，
     * 比如，analogueDataList值为[1,2,3,4,6,7]
     * channelStepNum为4，将分组来[[1,2,3,4][6,7]]
     * @param analogueDataList  模拟通道列表
     * @param channelStepNum    通道步长，该值都是2的N次方,N的赋值范围是(0,1,2,3,4)
     * @return
     */
    private static List<List<RequestCodeExecute.InputChannel>> groupingChannelByStep(
            List<RequestCodeExecute.InputChannel> analogueDataList,
            Integer channelStepNum){
        /**总组数量**/
        int groupNum =(int) Math.ceil(1.0 * analogueDataList.size() / channelStepNum);
        List<List<RequestCodeExecute.InputChannel>> groupList = new ArrayList();
        for(int j=0;j<groupNum;j++){
            List<RequestCodeExecute.InputChannel> list = new ArrayList<>();
            for(int i=0;i< analogueDataList.size();i++){
                if( channelStepNum * j <=i &&  i < channelStepNum * (j+1) ){
                    RequestCodeExecute.InputChannel analogue = analogueDataList.get(i);
                    list.add(analogue);
                }
            }
            groupList.add(list);
        }
        return groupList;
    }


}